using System;
using System.Text;
using System.Collections.Generic;
using CLELCore;
using CLELCore.Assembly;
using CLELCore.Assembly.CLELInstructions;
using CLELCore.Assembly.StreamTables;
using CLELCore.Assembly.StreamTables.StreamTableRows;
using CLELCore.Executable;

namespace CLELCore.Assembly.Printer.AssemblyConsolePrinter
{
	public class AssemblyConsolePrinter : ICLELPrinter
	{
		public const int ALL_FIELDS = 0x0;
		public const int CODE_ONLY = 0x1;

		private int _flags;
		private CLELAssembly _asm;

		public AssemblyConsolePrinter()
		{
			_flags = ALL_FIELDS;
		}

		public AssemblyConsolePrinter(int flags)
		{
			if(flags != ALL_FIELDS && flags != CODE_ONLY)
				throw new ArgumentException("Incorrect flags for console printer");
			_flags = flags;
		}
		
		public void Print(CLELAssembly asm)
		{
			_asm = asm;

			if(_flags == ALL_FIELDS)
			{
				PrintDOSHeader();
				byte[] signature = _asm.Signature;
				//print signature
				if(signature != null && signature.Length == 4 && 
					signature[0] == 0x50 && signature[1] == 0x45 && 
					signature[2] == 0x0 && signature[3] == 0x0)
				{
					PrintPEHeader();
				}

				PrintStandardFields();
				PrintNTSpecific();
				PrintDataDirectories();
				PrintSectionHeaders();
				PrintSections();
			}
			else if(_flags == CODE_ONLY)
			{
				TypeDefTable td = (TypeDefTable)asm.TextSection.PoundTildaStream.Tables[StreamTable.TYPEDEF_TABLE];
				MethodDefTable md = (MethodDefTable)asm.TextSection.PoundTildaStream.Tables[StreamTable.METHODDEF_TABLE];
				TypeDefTableRow td_row,td_row2;
				MethodDefTableRow md_row;
				int td_count = td.Count;
				int md_count = md.Count;
				int i,j,start,stop;
				String name,namespaces,m_name;
				for(i = 0;i < td_count;i++)
				{
					td_row = td.Get(i);
					name = ASCIIEncoding.ASCII.GetString(asm.TextSection.StringsStream.Get(td_row.Name));
					name = name.Substring(0,name.Length-1);
					if(!name.Equals("<Module>"))
					{
						namespaces = ASCIIEncoding.ASCII.GetString(asm.TextSection.StringsStream.Get(td_row.Namespace));
						namespaces = namespaces.Substring(0,namespaces.Length-1);
						start = td_row.MethodList-1;
						if(i < td_count - 1)
						{
							td_row2 = td.Get(i+1);
							stop = td_row2.MethodList-1;
						}
						else
						{
							stop = md_count - 1;
						}
						if(!namespaces.Equals(""))
							Console.WriteLine(namespaces+ "." + name);
						else
							Console.WriteLine(name);
						MethodHeader mh;
						for(j = start;j <= stop;j++)
						{
							md_row = md.Get(j);
							m_name = ASCIIEncoding.ASCII.GetString(asm.TextSection.StringsStream.Get(md_row.Name));
							m_name = m_name.Substring(0,m_name.Length-1);
							Console.WriteLine("\t"+m_name);
							mh = md_row.MethodHeader;
							if(mh != null)
								PrintCode(mh.Code);
						}
					}
				}
			}
		}

		private void PrintCode(List<CLELInstruction> code)
		{
			int i;
			CLELInstruction inst;
			int offset = 0;
			for(i = 0;i < code.Count;i++)
			{
				inst = code[i];
				Console.WriteLine("\t\t"+offset+": "+inst);
				offset += inst.Length;
			}
		}

		/*
		private String LongHexFormat(long val)
		{
			return String.Format("{0:X16}",val);
		}

		private String IntHexFormat(int val)
		{
			return String.Format("{0:X8}",val);
		}
		*/
		private String ShortHexFormat(short val)
		{
			return String.Format("{0:X4}",val);
		}

		private String ByteHexFormat(byte val)
		{
			return String.Format("{0:X2}",val);
		}

		private void PrintDOSHeader()
		{
			DOSHeader dos_header = _asm.DOSHeader;

			Console.WriteLine("DOSHeader");
			Console.WriteLine("Magic Number: 0x" + ShortHexFormat(dos_header.MagicNumber));
			Console.WriteLine("Last Page Of File: " + dos_header.LastPageOfFile);
			Console.WriteLine("Pages In File: " + dos_header.PagesInFile);
			Console.WriteLine("Relocations: " + dos_header.Relocations);
			Console.WriteLine("Size Of Header: " + dos_header.SizeOfHeader);
			Console.WriteLine("Min Paragraphs: " + dos_header.MinParagraphs);
			Console.WriteLine("Max Paragraphs: " + dos_header.MaxParagraphs);
			Console.WriteLine("Initial SS Value: " + dos_header.InitialSSValue);
			Console.WriteLine("Initial SP Value: " + dos_header.InitialSPValue);
			Console.WriteLine("Checksum: " + dos_header.Checksum);
			Console.WriteLine("Initial IP Value: " + dos_header.InitialIPValue);
			Console.WriteLine("Initial CS Value: " + dos_header.InitialCSValue);
			Console.WriteLine("Relocations Table: " + dos_header.RelocationsTable);
			Console.WriteLine("Overlay Number: " + dos_header.OverlayNumber);
			byte [] temp = dos_header.Reserved;
			int i;
			Console.Write("Reserved: ");
			for(i = 0;i < temp.Length;i++)
			{
				Console.Write(ByteHexFormat(temp[i])+ " ");
			}
			Console.Write("\n");
			Console.WriteLine("OEM Identifier: " + dos_header.OEMIdentifier);
			Console.WriteLine("OEM Information: " + dos_header.OEMInformation);
			temp = dos_header.Reserved2;
			Console.Write("Reserved2: ");
			for(i = 0;i < temp.Length;i++)
			{
				Console.Write(ByteHexFormat(temp[i])+ " ");
			}
			Console.Write("\n");
			Console.WriteLine("Signature Offset: " + dos_header.SignatureOffset);
			temp = dos_header.DOSStub;
			Console.WriteLine("DOS Stub:");
			for(i = 0;i < temp.Length;i++)
			{
				Console.Write(ByteHexFormat(temp[i])+" ");
				if(i % 16 == 15)
					Console.Write("\n");
			}
			Console.Write("\n");
		}

		private void PrintPEHeader()
		{
		}

		private void PrintStandardFields()
		{
		}

		private void PrintNTSpecific()
		{
		}

		private void PrintDataDirectories()
		{
		}

		private void PrintSectionHeaders()
		{
		}

		private void PrintSections()
		{
			//print .text
			//print rest (.rsrc,.reloc)
		}
	}
}
